package me.wangqu.weiyouqu.web.rest;

import com.codahale.metrics.annotation.Timed;

import com.taobao.api.response.AlibabaAliqinFcSmsNumSendResponse;
import me.wangqu.weiyouqu.domain.Captcha;
import me.wangqu.weiyouqu.domain.CaptchaImage;
import me.wangqu.weiyouqu.domain.User;
import me.wangqu.weiyouqu.repository.CaptchaImageRepository;
import me.wangqu.weiyouqu.repository.CaptchaRepository;
import me.wangqu.weiyouqu.repository.UserRepository;
import me.wangqu.weiyouqu.security.SecurityUtils;
import me.wangqu.weiyouqu.service.MailService;
import me.wangqu.weiyouqu.service.UserService;
import me.wangqu.weiyouqu.web.rest.dto.KeyAndPasswordDTO;
import me.wangqu.weiyouqu.web.rest.dto.ManagedUserDTO;
import me.wangqu.weiyouqu.web.rest.dto.UserDTO;
import me.wangqu.weiyouqu.web.rest.errors.ErrorConstants;
import me.wangqu.weiyouqu.web.rest.business.model.UserModel;
import me.wangqu.weiyouqu.web.rest.business.service.CustomUserService;
import me.wangqu.weiyouqu.web.rest.util.CaptchaUtil;
import me.wangqu.weiyouqu.web.rest.util.HeaderUtil;

import me.wangqu.weiyouqu.web.rest.util.HttpUtil;
import me.wangqu.weiyouqu.web.rest.util.RegexUtil;
import me.wangqu.weiyouqu.web.rest.util.constant.Constant;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import javax.imageio.ImageIO;
import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.time.ZonedDateTime;
import java.util.*;

/**
 * REST controller for managing the current user's account.
 */
@RestController
@RequestMapping("/api")
public class AccountResource {

    private final Logger log = LoggerFactory.getLogger(AccountResource.class);

    @Inject
    private UserRepository userRepository;

    @Inject
    private UserService userService;

    @Inject
    private MailService mailService;

    @Inject
    private CaptchaImageRepository captchaImageRepository;

    @Inject
    private CaptchaRepository captchaRepository;

    @Inject
    private CustomUserService customUserService;

    /**
     * 获取图形验证码
     *
     * @param phone
     * @param response
     */
    @RequestMapping(value = "/captImage/{phone}",
        method = RequestMethod.GET)
    @Timed
    public void captchaImage(@PathVariable String phone, HttpServletResponse response) {
        if (!RegexUtil.isMobile(phone)) {
            response.setStatus(HttpStatus.BAD_REQUEST.value());
//            try {
//                response.setHeader("Content-type", "text/html;charset=UTF-8"); //指定消息头以UTF-8码表读数据
//                OutputStream out = response.getOutputStream();
//                out.write("手机号码格式不对".getBytes("UTF-8"));
//
//            } catch (IOException e) {
//            }
            return;
        }
        //创建图片的不buff内存缓存区
        BufferedImage bi = new BufferedImage(68, 22, BufferedImage.TYPE_INT_RGB);
        //获取图像的处理接口
        Graphics GImage = bi.getGraphics();
        //创建图片背景色
        Color color = new Color(53, 181, 88);
        //设置图片颜色
        GImage.setColor(color);
        //与canvas 差不多  用来 用当前颜色填充 矩形
        GImage.fillRect(0, 0, 68, 22);

        char[] ca = "0123456789".toCharArray();
        Random r = new Random();  //随机默认当前系统时间对应的相对时间有关的数字作为种子数:
        int len = ca.length;
        int index;
        StringBuilder sb = new StringBuilder();
        Font f = new Font(null, Font.BOLD, 24);
        GImage.setFont(f);

        for (int i = 0; i < 4; i++) {    //随机生成四个数字
            index = r.nextInt(len);
//            GImage.setColor(new Color(r.nextInt(88), r.nextInt(188), r.nextInt(255)));
            GImage.setColor(new Color(255, 251, 240));
            if (i == 2) {
                Graphics2D g2d = (Graphics2D) GImage;
                g2d.rotate(0.02 * Math.PI);//旋转

            }
            GImage.drawString(ca[index] + "", i * 10 + 10, 18);   //设置每个字符的位置
            sb.append(ca[index]);
        }
        try {
            CaptchaImage capt = captchaImageRepository.findOneByPhone(phone).map(captImage -> {
                return captImage;
            }).orElseGet(() -> {
                CaptchaImage img = new CaptchaImage();
                img.setPhone(phone);
                return img;
            });
            capt.setCode(sb.toString());
            captchaImageRepository.save(capt);
            response.setContentType(MediaType.IMAGE_JPEG_VALUE);
            ImageIO.write(bi, "JPG", response.getOutputStream());
        } catch (IOException e) {
            log.debug(e.getMessage());
            return;
        }
    }

    @RequestMapping(value = "/sendSms",
        method = RequestMethod.POST,
        produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.TEXT_PLAIN_VALUE})
    @Timed
    public ResponseEntity<?> sendSms(@Valid @RequestBody Captcha captcha, HttpServletRequest request) {
        HttpHeaders textPlainHeaders = new HttpHeaders();
        textPlainHeaders.setContentType(MediaType.TEXT_PLAIN);
        return captchaImageRepository.findOneByPhone(captcha.getPhone()).map(captImage -> {
            // 手机对应图形验证码存在
            if (StringUtils.isEmpty(captcha.getCode()) || !captcha.getCode().equalsIgnoreCase(captImage.getCode())) {
                // 用户输入的值与系统记录值不一致
                return new ResponseEntity<>(ErrorConstants.ERR_USER_1001, textPlainHeaders, HttpStatus.BAD_REQUEST);
            }
            // 判断手机是否存在
            boolean exist = userRepository.findOneByLogin(captcha.getPhone()).map(user -> {
                return true;
            }).orElse(false);
            if (exist && captcha.getType() == Constant.CAPTCHA_TYPE_REGISTER) {
                return new ResponseEntity<>(ErrorConstants.ERR_USER_1002, textPlainHeaders, HttpStatus.BAD_REQUEST);
            }
            if (!exist && captcha.getType() == Constant.CAPTCHA_TYPE_PASSWORD_RESET) {
                return new ResponseEntity<>(ErrorConstants.ERR_USER_1003, textPlainHeaders, HttpStatus.BAD_REQUEST);
            }
            return captchaRepository.findOneByPhoneAndType(captcha.getPhone(), captcha.getType()).map(old -> {
                // 验证码存在
                // 超过60秒允许重新发送
                ZonedDateTime now = ZonedDateTime.now();
                if (ZonedDateTime.now().compareTo(old.getTime().plusSeconds(60)) > 0) {
                    old.setCode(CaptchaUtil.randomCode());
                    old.setTime(ZonedDateTime.now());
                    AlibabaAliqinFcSmsNumSendResponse response = CaptchaUtil.sendSms(old.getPhone(), old.getCode(), old.getType());
                    if (response != null && response.isSuccess()) {
                        // 记录手机号码对应的验证码
                        captchaRepository.save(old);
                        return new ResponseEntity<String>(HttpStatus.OK);
                    } else {
                        return new ResponseEntity<>(ErrorConstants.ERR_USER_1004, textPlainHeaders, HttpStatus.BAD_REQUEST);
                    }
                }
                // 60秒内重复发送
                return new ResponseEntity<>(ErrorConstants.ERR_USER_1000, textPlainHeaders, HttpStatus.BAD_REQUEST);
            }).orElseGet(() -> {
                // 该手机未发送过该类型的手机验证码
                captcha.setCode(CaptchaUtil.randomCode());
                captcha.setTime(ZonedDateTime.now());
                AlibabaAliqinFcSmsNumSendResponse response = CaptchaUtil.sendSms(captcha.getPhone(), captcha.getCode(), captcha.getType());
                if (response != null && response.isSuccess()) {
                    captchaRepository.save(captcha);
                    return new ResponseEntity<>(HttpStatus.OK);
                }
                return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
            });
        }).orElseGet(() -> {
            return new ResponseEntity<>(ErrorConstants.ERR_USER_1001, textPlainHeaders, HttpStatus.BAD_REQUEST);
        });
    }

    @RequestMapping(value = "/register",
        method = RequestMethod.POST,
        produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.TEXT_PLAIN_VALUE})
    @Timed
    public ResponseEntity<?> registerAccount(@Valid @RequestBody UserModel model, HttpServletRequest request) {

        HttpHeaders textPlainHeaders = new HttpHeaders();
        textPlainHeaders.setContentType(MediaType.TEXT_PLAIN);
        if (StringUtils.isEmpty(model.getCode())) {
            return new ResponseEntity<>("验证码不能为空", textPlainHeaders, HttpStatus.BAD_REQUEST);
        }
        return captchaRepository.findOneByPhoneAndType(model.getLogin(), Constant.CAPTCHA_TYPE_REGISTER).map(captcha -> {
            if (captcha.getCode().equals(model.getCode())) {
                return userRepository.findOneByLogin(model.getLogin())
                    .map(user -> new ResponseEntity<>("手机已经注册", textPlainHeaders, HttpStatus.BAD_REQUEST))
                    .orElseGet(() -> {
                        if (!StringUtils.isEmpty(model.getEmail()) && userRepository.findOneByEmail(model.getEmail()) != null) {
                            return new ResponseEntity<>("邮箱已经被使用", textPlainHeaders, HttpStatus.BAD_REQUEST);
                        }
                        // 创建用户
                        model.setLangKey(StringUtils.isEmpty(model.getLangKey()) ? "zh-cn" : model.getLangKey());
                        User user = userService.createUserInformation(model.getLogin(), model.getPassword(),
                            model.getFirstName(), model.getLastName(), model.getEmail(),
                            model.getLangKey());
                        // WEB 端用户创建
                        model.setIp(HttpUtil.getClientIP(request)); // 设置用户 IP 地址
                        customUserService.createUser(model);
                        return new ResponseEntity<>(HttpStatus.CREATED);
                    });
            }
            return new ResponseEntity<>("验证码错误", textPlainHeaders, HttpStatus.BAD_REQUEST);
        }).orElse(new ResponseEntity<>("验证码未发送", textPlainHeaders, HttpStatus.BAD_REQUEST));
    }

    @RequestMapping(value = "/account/reset_password",
        method = RequestMethod.POST,
        produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.TEXT_PLAIN_VALUE})
    @Timed
    public ResponseEntity<?> changePasswordByCode(@Valid @RequestBody UserModel model) {
        HttpHeaders textPlainHeaders = new HttpHeaders();
        textPlainHeaders.setContentType(MediaType.TEXT_PLAIN);
        if (!checkPasswordLength(model.getPassword())) {
            return new ResponseEntity<>("密码长度错误", textPlainHeaders, HttpStatus.BAD_REQUEST);
        }
        if (StringUtils.isEmpty(model.getCode())) {
            return new ResponseEntity<>("验证码不能为空", textPlainHeaders, HttpStatus.BAD_REQUEST);
        }
        // 校验验证码
        return captchaRepository.findOneByPhoneAndType(model.getLogin(), Constant.CAPTCHA_TYPE_PASSWORD_RESET).map(captcha -> {
            if (captcha.getCode().equals(model.getCode())) {
                // CHANGE PASSWORD
                return userRepository.findOneByLogin(model.getLogin()).map(u -> {
                    u.setPassword(model.getPassword());
                    userRepository.save(u);
                    // 修改 WEB 用户密码
                    customUserService.changePassword(model);
                    return new ResponseEntity<>(HttpStatus.OK);
                }).orElse(new ResponseEntity<>("帐户不存在", textPlainHeaders, HttpStatus.BAD_REQUEST));
            }
            return new ResponseEntity<>("验证码错误", textPlainHeaders, HttpStatus.BAD_REQUEST);
        }).orElse(new ResponseEntity<>("验证码未发送", textPlainHeaders, HttpStatus.BAD_REQUEST));
    }

    /**
     * POST  /register : register the user.
     *
     * @param managedUserDTO the managed user DTO
     * @param request the HTTP request
     * @return the ResponseEntity with status 201 (Created) if the user is registred or 400 (Bad Request) if the login or e-mail is already in use
     */
//    @RequestMapping(value = "/register",
//                    method = RequestMethod.POST,
//                    produces={MediaType.APPLICATION_JSON_VALUE, MediaType.TEXT_PLAIN_VALUE})
//    @Timed
//    public ResponseEntity<?> registerAccount(@Valid @RequestBody ManagedUserDTO managedUserDTO, HttpServletRequest request) {
//
//        HttpHeaders textPlainHeaders = new HttpHeaders();
//        textPlainHeaders.setContentType(MediaType.TEXT_PLAIN);
//
//        return userRepository.findOneByLogin(managedUserDTO.getLogin().toLowerCase())
//            .map(user -> new ResponseEntity<>("login already in use", textPlainHeaders, HttpStatus.BAD_REQUEST))
//            .orElseGet(() -> userRepository.findOneByEmail(managedUserDTO.getEmail())
//                .map(user -> new ResponseEntity<>("e-mail address already in use", textPlainHeaders, HttpStatus.BAD_REQUEST))
//                .orElseGet(() -> {
//                    User user = userService.createUserInformation(managedUserDTO.getLogin(), managedUserDTO.getPassword(),
//                    managedUserDTO.getFirstName(), managedUserDTO.getLastName(), managedUserDTO.getEmail().toLowerCase(),
//                    managedUserDTO.getLangKey());
//                    String baseUrl = request.getScheme() + // "http"
//                    "://" +                                // "://"
//                    request.getServerName() +              // "myhost"
//                    ":" +                                  // ":"
//                    request.getServerPort() +              // "80"
//                    request.getContextPath();              // "/myContextPath" or "" if deployed in root context
//
//                    mailService.sendActivationEmail(user, baseUrl);
//                    return new ResponseEntity<>(HttpStatus.CREATED);
//                })
//        );
//    }

    /**
     * GET  /activate : activate the registered user.
     *
     * @param key the activation key
     * @return the ResponseEntity with status 200 (OK) and the activated user in body, or status 500 (Internal Server Error) if the user couldn't be activated
     */
    @RequestMapping(value = "/activate",
        method = RequestMethod.GET,
        produces = MediaType.APPLICATION_JSON_VALUE)
    @Timed
    public ResponseEntity<String> activateAccount(@RequestParam(value = "key") String key) {
        return userService.activateRegistration(key)
            .map(user -> new ResponseEntity<String>(HttpStatus.OK))
            .orElse(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR));
    }

    /**
     * GET  /authenticate : check if the user is authenticated, and return its login.
     *
     * @param request the HTTP request
     * @return the login if the user is authenticated
     */
    @RequestMapping(value = "/authenticate",
        method = RequestMethod.GET,
        produces = MediaType.APPLICATION_JSON_VALUE)
    @Timed
    public String isAuthenticated(HttpServletRequest request) {
        log.debug("REST request to check if the current user is authenticated");
        return request.getRemoteUser();
    }

    /**
     * GET  /account : get the current user.
     *
     * @return the ResponseEntity with status 200 (OK) and the current user in body, or status 500 (Internal Server Error) if the user couldn't be returned
     */
    @RequestMapping(value = "/account",
        method = RequestMethod.GET,
        produces = MediaType.APPLICATION_JSON_VALUE)
    @Timed
    public ResponseEntity<UserDTO> getAccount() {
        return Optional.ofNullable(userService.getUserWithAuthorities())
            .map(user -> new ResponseEntity<>(new UserDTO(user), HttpStatus.OK))
            .orElse(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR));
    }

    /**
     * POST  /account : update the current user information.
     *
     * @param userDTO the current user information
     * @return the ResponseEntity with status 200 (OK), or status 400 (Bad Request) or 500 (Internal Server Error) if the user couldn't be updated
     */
    @RequestMapping(value = "/account",
        method = RequestMethod.POST,
        produces = MediaType.APPLICATION_JSON_VALUE)
    @Timed
    public ResponseEntity<String> saveAccount(@Valid @RequestBody UserDTO userDTO) {
        Optional<User> existingUser = userRepository.findOneByEmail(userDTO.getEmail());
        if (existingUser.isPresent() && (!existingUser.get().getLogin().equalsIgnoreCase(userDTO.getLogin()))) {
            return ResponseEntity.badRequest().headers(HeaderUtil.createFailureAlert("user-management", "emailexists", "Email already in use")).body(null);
        }
        return userRepository
            .findOneByLogin(SecurityUtils.getCurrentUserLogin())
            .map(u -> {
                userService.updateUserInformation(userDTO.getFirstName(), userDTO.getLastName(), userDTO.getEmail(),
                    userDTO.getLangKey());
                return new ResponseEntity<String>(HttpStatus.OK);
            })
            .orElseGet(() -> new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR));
    }

    /**
     * POST  /account/change_password : changes the current user's password
     *
     * @param password the new password
     * @return the ResponseEntity with status 200 (OK), or status 400 (Bad Request) if the new password is not strong enough
     */
    @RequestMapping(value = "/account/change_password",
        method = RequestMethod.POST,
        produces = MediaType.TEXT_PLAIN_VALUE)
    @Timed
    public ResponseEntity<?> changePassword(@RequestBody String password) {
        if (!checkPasswordLength(password)) {
            return new ResponseEntity<>("Incorrect password", HttpStatus.BAD_REQUEST);
        }
        userService.changePassword(password);
        return new ResponseEntity<>(HttpStatus.OK);
    }

    /**
     * POST   /account/reset_password/init : Send an e-mail to reset the password of the user
     *
     * @param mail    the mail of the user
     * @param request the HTTP request
     * @return the ResponseEntity with status 200 (OK) if the e-mail was sent, or status 400 (Bad Request) if the e-mail address is not registred
     */
    @RequestMapping(value = "/account/reset_password/init",
        method = RequestMethod.POST,
        produces = MediaType.TEXT_PLAIN_VALUE)
    @Timed
    public ResponseEntity<?> requestPasswordReset(@RequestBody String mail, HttpServletRequest request) {
        return userService.requestPasswordReset(mail)
            .map(user -> {
                String baseUrl = request.getScheme() +
                    "://" +
                    request.getServerName() +
                    ":" +
                    request.getServerPort() +
                    request.getContextPath();
                mailService.sendPasswordResetMail(user, baseUrl);
                return new ResponseEntity<>("e-mail was sent", HttpStatus.OK);
            }).orElse(new ResponseEntity<>("e-mail address not registered", HttpStatus.BAD_REQUEST));
    }

    /**
     * POST   /account/reset_password/finish : Finish to reset the password of the user
     *
     * @param keyAndPassword the generated key and the new password
     * @return the ResponseEntity with status 200 (OK) if the password has been reset,
     * or status 400 (Bad Request) or 500 (Internal Server Error) if the password could not be reset
     */
    @RequestMapping(value = "/account/reset_password/finish",
        method = RequestMethod.POST,
        produces = MediaType.TEXT_PLAIN_VALUE)
    @Timed
    public ResponseEntity<String> finishPasswordReset(@RequestBody KeyAndPasswordDTO keyAndPassword) {
        if (!checkPasswordLength(keyAndPassword.getNewPassword())) {
            return new ResponseEntity<>("Incorrect password", HttpStatus.BAD_REQUEST);
        }
        return userService.completePasswordReset(keyAndPassword.getNewPassword(), keyAndPassword.getKey())
            .map(user -> new ResponseEntity<String>(HttpStatus.OK))
            .orElse(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR));
    }

    private boolean checkPasswordLength(String password) {
        return (!StringUtils.isEmpty(password) &&
            password.length() >= ManagedUserDTO.PASSWORD_MIN_LENGTH &&
            password.length() <= ManagedUserDTO.PASSWORD_MAX_LENGTH);
    }
}
